#include "UIniFile.h"

#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>

using namespace std;

TIniFile::TIniFile(const string & FileName)
{
	Load(FileName);
}

TIniFile::TIniFile(ifstream & stream) {
	Load(stream);
}

TIniFile::TIniFile()
{
	file = "";
}

TIniFile::~TIniFile()
{
}

void TIniFile::addSection(const string & sectionname)
{
	if (hasSection(sectionname)) return;
	INISectionRef sect(new INISection(sectionname));
	sect->name = sectionname;
	inidata.get<1>().push_back(sect); 
}

void TIniFile::removeSection(const string & sectionname)
{
	//if (!hasSection(sectionname)) return;
	//TODO: test if above is really not needed
	inidata.erase(sectionname);
}

bool TIniFile::hasSection(const string & sectionname) const
{
	return inidata.count(sectionname) > 0;
	// TODO: do above more direct?
}

void TIniFile::setValue(const string & sectionname, const string & keyname, const string &  valuename)
{
	addSection(sectionname);
	INISectionRef sect = *inidata.find(sectionname);
	if (keyname == "" || !hasValue(sectionname, keyname)) {
		INIKeyValuePairRef kv(new INIKeyValuePair(keyname, valuename));
		sect->keyvals.get<1>().push_back(kv);
	} else {
		INIKeyValuePairRef kv = *sect->keyvals.find(keyname);
		kv->value = valuename;
	}
}

string TIniFile::getValue(const string & sectionname, const string & keyname) const
{
	if (!hasValue(sectionname, keyname)) return "";
	INISectionRef sect = *inidata.find(sectionname);
	INIKeyValuePairRef kv = *sect->keyvals.find(keyname);
	return kv->value;
}

bool TIniFile::hasValue(const string & sectionname, const string & keyname) const
{
	if (!hasSection(sectionname)) return false;
	INISectionRef sect = *inidata.find(sectionname);
	return sect->keyvals.count(keyname) > 0;
}

bool TIniFile::Load( const std::string & FileName ) {
	ifstream inFile( FileName.c_str() );    // Create an input filestream
	bool ret = Load(inFile);
	file = FileName;
	return ret;
}

bool TIniFile::Load( ifstream & inFile ) {
	file = "";
	clear(); // clear previous data
	string s;																// Holds the current line from the ini file
	string CurrentSection;													// Holds the current section name

	if ( !inFile.is_open() ) return false;									// If the input file doesn't open, then return

	// Read until the end of the file (including last line, even if it has no lineend
	for ( ; !getline( inFile, s ).eof() || !s.empty() ; s.clear())
	{
		boost::trim( s );															// Trim whitespace from the ends

		if (s.empty() || ( s[0]=='#' ) || ( s[0]==';' ))
		{
			setValue(CurrentSection, "", s);
			continue;
		}

		if ( (s[0] == '[') || (s[s.size()-1] == ']') )									// Is this line a section?
		{
			CurrentSection = string(s.begin()+1, s.end()-1);
			continue;
		}

		if ( s.find( '=' )!=string::npos )									// Is this line a Key/Value?
		{
			setValue(CurrentSection,
				boost::trim_copy(s.substr( 0,s.find( '=' ) )) ,
				boost::trim_copy(s.substr( s.find( '=' )+1 )) );
		}
	}

	return true;
}

bool TIniFile::Save( const string & FileName ) const //FIXME: Needs an ofstream counterpart
{
	ofstream outFile( FileName.c_str() );										// Create an output filestream
	if ( !outFile.is_open() ) return false;									// If the input file doesn't open, then return

	BOOST_FOREACH(const INISectionRef & sect, inidata.get<1>()) {	
		if (sect->name != "")
			outFile << '[' << sect->name << ']' << '\n';

		BOOST_FOREACH(const INIKeyValuePairRef & keyvalue, sect->keyvals.get<1>()) {
			if (keyvalue->key != "")
				outFile << keyvalue->key << " = ";
			outFile << keyvalue->value << '\n';
		}
	}
	outFile.close();
	return true;
}

bool TIniFile::Save() const
{
	if (file != "")
		return Save(file);
	else
		return false;
}

void TIniFile::clear()
{
	inidata.clear();
}
